#include "crypt.h"
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <memory.h>

void random_generate_aeskey(char* key, int len) {
	srand(time(NULL));
	for (int i = 0; i < len; i++) {
		int flag = rand() % 3;
		switch (flag) {
		case 0:
			key[i] = rand() % 26 + 'a'; break;
		case 1:
			key[i] = rand() % 26 + 'A'; break;
		default:
			key[i] = rand() % 10 + '0'; break;
		}
	}
	key[len] = '\0';
}

void generate_aeskey_by_passwd(char *password,size_t password_len,char *key) {
  SHA256((unsigned char*)password,password_len,(unsigned char*)key);
}

int aes_encrypt(const char* src,size_t src_len,char *to,const char *key,char *ivec){
  AES_KEY aes_key;
  memset(&aes_key, 0x00, sizeof(aes_key));
  if(AES_set_encrypt_key((unsigned char*)key, AES_KEY_LEN, &aes_key) < 0) {
    fprintf(stderr, "Unable to set encryption key in AES\n");
		return -1;
	}
	AES_cbc_encrypt((unsigned char*)src, (unsigned char*)to, src_len, &aes_key,(unsigned char*)ivec,AES_ENCRYPT);
	return (src_len + 15) / 16 * 16;
}

int aes_decrypt(const char *src,size_t src_len,char *to,const char *key,char *ivec) {
  AES_KEY aes_key;
	memset(&aes_key, 0x00, sizeof(aes_key));
	if (AES_set_decrypt_key((unsigned char*)key, AES_KEY_LEN, &aes_key) < 0) {
		fprintf(stderr, "Unable to set encryption key in AES\n");
		return -1;
	}
	AES_cbc_encrypt((unsigned char*)src, (unsigned char*)to, src_len, &aes_key, (unsigned char*)ivec,AES_DECRYPT);
	return 0;
}

int rsa_generate_key_files(const char *pub_key_filename, const char *priv_key_filename,	const char *passwd) {
	int ret = 0;
	RSA *rsa = NULL;
	BIGNUM *bne = NULL;
	BIO * bp_public = NULL, *bp_private = NULL;
	int bits = RSA_KEY_LENGTH;
	unsigned long e = RSA_F4;

	bne = BN_new();
	if ((ret = BN_set_word(bne, e)) != 1) {
		goto free_all;
	}
	rsa = RSA_new();
	if ((ret = RSA_generate_key_ex(rsa, bits, bne, NULL)) != 1) {
		goto free_all;
	}

	/*保存公钥到文件*/
	bp_public = BIO_new_file(pub_key_filename, "w+");
	if ((ret = PEM_write_bio_RSAPublicKey(bp_public, rsa)) != 1) {
		goto free_all;
	}

	/*将私钥以3des加密后写到文件*/
	bp_private = BIO_new_file(priv_key_filename, "w+");
	if ((ret = PEM_write_bio_RSAPrivateKey(bp_private, rsa, EVP_aes_256_cbc(),
			(unsigned char*) passwd, (int) strlen((const char*) passwd), NULL,
			NULL)) != 1) {
		goto free_all;
	}

	free_all: BIO_free_all(bp_public);
	BIO_free_all(bp_private);
	RSA_free(rsa);
	BN_free(bne);

	return ret == 1 ? 0 : -1;
}

int read_key_file(const char *filename,char *buf) {
  int fd = open(filename,O_RDONLY);
  if(fd<0) {
    printf("打开文件%s失败\n", filename);
    return -1;
  }
  //默认密钥长度为2048bit，对应的私钥文件长度不到2048字节，这里取整读取2048字节
  int len = read(fd,buf,2048);
  if(len<0) {
    printf("读取文件%s失败\n",filename );
    close(fd); return -1;
  }
  buf[len]='\0';
  return 0;
}

int rsa_pom_passwd_callback(char *buf, int max_len, int flag, void *ctx) {
	char pin_buf[128];
	int pin_len;

	printf("请输入私钥密码:\n");
	fgets((char*) pin_buf, sizeof(pin_buf), stdin);
	pin_buf[strlen(pin_buf) - 1] = '\0';

	pin_len = strlen((char*) pin_buf);
	if (pin_len > max_len)
		return 0;
	memcpy(buf, pin_buf, pin_len);
	return pin_len;
}

RSA* rsa_create_RSA(const char* key, int pub_flag) {
	RSA * rsa = NULL;
	BIO * key_bio = NULL;

	key_bio = BIO_new_mem_buf((void*) key, -1);
	if (!key_bio) {
		printf("新建BIO error\n");
		goto free_all;
	}

	if (pub_flag) {
		rsa = PEM_read_bio_RSAPublicKey(key_bio, &rsa, NULL, NULL);
	} else {
		rsa = PEM_read_bio_RSAPrivateKey(key_bio, &rsa, rsa_pom_passwd_callback,
		NULL);
	}

	if (!rsa) {
		printf("创建RSA失败\n");
		goto free_all;
	}

	free_all: BIO_free_all(key_bio);
	return rsa;
}

int rsa_pub_encrypt(const char *src, size_t src_len, char *to, const char *pub_key) {

  int encrypted_len = 0;
	int nleft = src_len;
	RSA * rsa = NULL;

	rsa = rsa_create_RSA(pub_key, 1);
	if (rsa) {
		/*在PKCS1填充方式中,每次最多加密256-11字节的明文*/
		int temp;
		while (nleft > 0) {
			temp = RSA_public_encrypt(nleft > 245 ? 245 : nleft,
					(unsigned char*) src + src_len - nleft,
					(unsigned char*) to + encrypted_len, rsa,
					RSA_PADDING);
			if (temp == -1) {
				openssl_log_error();
				encrypted_len = -1;
				goto free_all;
			}
			encrypted_len += temp;
			nleft -= 245;
		}

		free_all: RSA_free(rsa);
		return encrypted_len;
	}
	return -1;
}

int rsa_priv_decrypt(const char *src, size_t src_len, char *to, const char *priv_key) {

	int decrypted_len = 0;
	int nleft = src_len;
	RSA * rsa = NULL;

	rsa = rsa_create_RSA(priv_key, 0);
	if (rsa) {
		int temp;
		while (nleft > 0) {
			temp = RSA_private_decrypt(nleft > 256 ? 256 : nleft,
					(unsigned char*) src + src_len - nleft,
					(unsigned char*) to + decrypted_len, rsa,
					RSA_PADDING);
			if (temp == -1) {
				openssl_log_error();
				decrypted_len = -1;
				goto free_all;
			}
			decrypted_len += temp;
			nleft -= 256;
		}
		free_all: RSA_free(rsa);
		to[decrypted_len] = '\0';
		return decrypted_len;
	}
	return -1;
}

int rsa_priv_encrypt(const char *src, size_t src_len, char *to, const char *priv_key) {

	int encrypted_len = 0;
	int nleft = src_len;
	RSA * rsa = NULL;

	rsa = rsa_create_RSA(priv_key, 0);
	if (rsa) {
		/*在PKCS1填充方式中,每次最多加密256-11字节的明文*/
		int temp;
		while (nleft > 0) {
			temp = RSA_private_encrypt(nleft > 245 ? 245 : nleft,
					(unsigned char*) src + src_len - nleft,
					(unsigned char*) to + encrypted_len, rsa,
					RSA_PADDING);
			if (temp == -1) {
				openssl_log_error();
				encrypted_len = -1;
				goto free_all;
			}
			encrypted_len += temp;
			nleft -= 245;
		}
		free_all: RSA_free(rsa);
		return encrypted_len;
	}
	return -1;
}
int rsa_pub_decrypt(const char *src, size_t src_len, char *to, const char *pub_key) {

	int decrypted_len = 0;
	int nleft = src_len;
	RSA * rsa = NULL;

	rsa = rsa_create_RSA(pub_key, 1);
	if (rsa) {
		int temp;
		while (nleft > 0) {
			temp = RSA_public_decrypt(nleft > 256 ? 256 : nleft,
					(unsigned char*) src + src_len - nleft,
					(unsigned char*) to + decrypted_len, rsa,
					RSA_PADDING);
			if (temp == -1) {
				openssl_log_error();
				decrypted_len = -1;
				goto free_all;
			}
			decrypted_len += temp;
			nleft -= 256;
		}
		free_all: RSA_free(rsa);
		to[decrypted_len] = '\0';
		return decrypted_len;
	}
	return -1;
}

int sha256_file(char *path,char *hash) {
	int fd=open(path,O_RDONLY);
	if(fd<0) {
		printf("打开文件%s失败\n",path);
		return -1;
	}
	unsigned char t_hash[SHA256_DIGEST_LENGTH];
	char *buf = malloc(4096);
	if(!buf) return -1;
	SHA256_CTX sha256;
	SHA256_Init(&sha256);

	//计算hash
	int len;
	while((len=read(fd,buf,sizeof(buf)))>0) {
		SHA256_Update(&sha256, buf, len);
	}
	SHA256_Final(t_hash,&sha256);
	//把hansh值转化成16进制表示写在hash中
	for(int i=0;i<SHA256_DIGEST_LENGTH;i++) {
		hash[i*2]=t_hash[i]/16<10? '0'+t_hash[i]/16:'a'+t_hash[i]/16-10;
		hash[i*2+1]=t_hash[i]%16<10? '0'+t_hash[i]%16:'a'+t_hash[i]%16-10;
	}
	hash[2*SHA256_DIGEST_LENGTH]='\0';
	close(fd);
	free(buf);
	return 0;
}

void openssl_log_error() {
	unsigned long errorTrack = ERR_get_error();
	char error[256];
	ERR_error_string(errorTrack, (char*) error);
	printf("错误码:%s\n", (char*) error);
}
